﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections;
using System.Windows.Data;

namespace SwiftMVVM.Commands
{
    /// <summary>
    /// CollectionViewCommands is a set of standard commands used to maniplate the 
    /// CurrentItem on a CollectionView. Navigation and simple position setting.
    /// </summary>
    public static class CollectionViewCommands
    {
        /// <summary>
        /// Moves the currentItem to the last position in the view
        /// </summary>
        public static RelayCommand<object> MoveLast
        {
            get
            {
                //The first section here says to fire MoveCurrentToLast on the passed in ICollectionView
                //if it's not null. 
                //Thesecond section controls whether or not the command itself is enabled.
                //So here we only enable the command if the passed in ICollectionView is not null
                //and if and only if it actually not already positioned at the end of the view
                return new RelayCommand<object>(collection =>
                { ToICollectionView(collection).MoveCurrentToLast(); },
                collection =>
                {
                    if (collection == null) { return false; }
                    var collectionView = ToICollectionView(collection);
                    return ToICollection(collectionView).Count != (collectionView.CurrentPosition + 1);
                });
            }
        }

        /// <summary>
        /// Moves the currentItem to the first position in the view
        /// </summary>
        public static RelayCommand<object> MoveFirst
        {
            get
            {
                //The first section here says to fire MoveCurrentToFirst on the passed in ICollectionView
                //if it's not null. 
                //Thesecond section controls whether or not the command itself is enabled.
                //So here we only enable the command if the passed in ICollectionView is not null
                //and if and only if it actually not already positioned beginning of the view
                return new RelayCommand<object>(collection =>
                { ToICollectionView(collection).MoveCurrentToFirst(); },
                collection =>
                {
                    if (collection == null) { return false; }
                    var collectionView = ToICollectionView(collection);
                    return collectionView.CurrentItem != null && collectionView.CurrentPosition != 0;
                });
            }
        }

        /// <summary>
        /// Moves the currentItem to the next position in the view
        /// </summary>
        public static RelayCommand<object> MoveNext
        {
            get
            {
                //The first section here says to fire MoveCurrentToNext on the passed in ICollectionView
                //if it's not null. 
                //Thesecond section controls whether or not the command itself is enabled.
                //So here we only enable the command if the passed in ICollectionView is not null
                //and if and only if it actually not already positioned end of the view
                return new RelayCommand<object>(collection =>
                { ToICollectionView(collection).MoveCurrentToNext(); },
                collection =>
                {
                    if (collection == null) { return false; }
                    var collectionView = ToICollectionView(collection);
                    return ToICollection(collectionView).Count != (collectionView.CurrentPosition + 1);
                });
            }
        }

        /// <summary>
        /// Moves the current item to the prior position in the view
        /// </summary>
        public static RelayCommand<object> MovePrevious
        {
            get
            {
                //The first section here says to fire MoveCurrentToFirst on the passed in ICollectionView
                //if it's not null. 
                //Thesecond section controls whether or not the command itself is enabled.
                //So here we only enable the command if the passed in ICollectionView is not null
                //and if and only if it actually not already positioned beginning of the view
                return new RelayCommand<object>(collection =>
                { ToICollectionView(collection).MoveCurrentToPrevious(); },
                collection =>
                {
                    if (collection == null) { return false; }
                    var collectionView = ToICollectionView(collection);
                    return collectionView.CurrentItem != null && collectionView.CurrentPosition != 0;
                });
            }
        }

        /// <summary>
        /// Removes the currently selected item from the collection
        /// </summary>
        public static RelayCommand<object> RemoveCurrentItem
        {
            get
            {
                //The first section here says to fire Remove on the passed in IList
                //if it's not null for the CurrentItem in the view. 
                //Thesecond section controls whether or not the command itself is enabled.
                //So here we only enable the command if the passed in ICollectionView is not null
                //and if and only if the current item in the view is not null
                return new RelayCommand<object>(collection =>
                {
                    var collectionView = ToICollectionView(collection);
                    if (collectionView.CurrentItem != null)
                    {
                        var iList = ToIList(collectionView);
                        iList.Remove(collectionView.CurrentItem);
                    }
                },
                collection =>
                {
                    if (collection == null) { return false; }
                    return ToICollectionView(collection).CurrentItem != null;
                });
            }
        }

        /// <summary>
        /// Moves to the specified position on the passed in collection.
        /// The first parameter passed in here must be the collection, and the 
        /// second parameter passed in must be the desired position.
        /// </summary>
        public static RelayCommand<object[]> MoveToPosition
        {
            get
            {
                //The first section here says to fire MoveCurrentToPosition on the passed in CollectionView
                //Thesecond section controls whether or not the command itself is enabled.
                //So here we only enable the command if the passed in ICollectionView is not null
                //and if and only if the position requested is not outside the bounds of the collection.
                //We're also assuming a 1 based collection.
                return new RelayCommand<object[]>(parameters =>
                {
                    if (parameters == null || parameters.Length != 2) { throw new InvalidOperationException("Must pass no more than 2 parameters to ChangeManagerCommands.Undo"); }

                    var position = (parameters[1] as int?) ?? System.Convert.ToInt32(parameters[1]);
                    ToICollectionView(parameters[0]).MoveCurrentToPosition(position);
                },
                parameters =>
                {
                    if (parameters == null) { return false; }
                    if (parameters.Length != 2) { throw new InvalidOperationException("Must pass no more than 2 parameters to ChangeManagerCommands.Undo"); }

                    var collectionView = ToICollectionView(parameters[0]);
                    var requestedPosition = (parameters[1] as int?) ?? System.Convert.ToInt32(parameters[1]);

                    return requestedPosition >= 0 &&
                        requestedPosition < ToICollection(collectionView).Count &&
                        requestedPosition != collectionView.CurrentPosition;
                });
            }
        }

        /// <summary>
        /// Converts the passed in object to an ICollectionView if it's not already one.
        /// Also puts all of our error checking in one place so we don't have to 
        /// repeat it all over
        /// </summary>
        /// <param name="collection">Any collection object</param>
        /// <returns>Calls GetDefaultView on the passed in collection if it's not already an ICollectionView</returns>
        private static ICollectionView ToICollectionView(object collection)
        {
            if (collection == null) { throw new ArgumentNullException("collection"); }
            var collectionView = (collection as ICollectionView) ?? CollectionViewSource.GetDefaultView(collection);
            if (collectionView == null)
            {
                var objectDataProvidor = collection as ObjectDataProvider;
                if (objectDataProvidor != null)
                {
                    var data = objectDataProvidor.Data ?? objectDataProvidor.ObjectInstance;
                    collectionView = (data as ICollectionView) ?? CollectionViewSource.GetDefaultView(data);
                }
            }
            if (collectionView == null) { throw new ArgumentNullException("Could not get an ICollectionView from collection"); }
            return collectionView;
        }

        /// <summary>
        /// Converts the passed in ICollectionView's SourceCollection to an ICollection
        /// Also puts all of our error checking in one place so we don't have to 
        /// repeat it all over
        /// </summary>
        /// <param name="collection"></param>
        /// <returns></returns>
        private static ICollection ToICollection(ICollectionView iCollectionView)
        {
            if (iCollectionView == null) { throw new ArgumentNullException("collection"); }
            var iCollection = iCollectionView.SourceCollection as ICollection;
            if (iCollection == null) { throw new InvalidCastException("View.Source cannot be null and must derive from IList"); }
            return iCollection;
        }

        /// <summary>
        /// Converts the passed in ICollectionView's SourceCollection to an IList
        /// Also puts all of our error checking in one place so we don't have to 
        /// repeat it all over
        /// </summary>
        /// <param name="collection"></param>
        /// <returns></returns>
        private static IList ToIList(ICollectionView iCollectionView)
        {
            if (iCollectionView == null) { throw new ArgumentNullException("collection"); }
            var iList = iCollectionView.SourceCollection as IList;
            if (iList == null) { throw new InvalidCastException("View.Source cannot be null and must derive from IList"); }
            return iList;
        }
    }
}
